package org.eden.heartbeat.api.jmx;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eden.heartbeat.core.configuration.CoreApplicationConfiguration;
import org.eden.heartbeat.api.exception.ReportException;
import org.eden.heartbeat.api.model.MachineInfo;
import org.eden.heartbeat.api.model.OfflinePeriod;
import org.eden.heartbeat.api.model.Request;
import org.eden.heartbeat.core.persistence.RequestDAO;
import org.eden.heartbeat.core.persistence.SimplePersistenceManager;

import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * The MBean service that processes statistics requests.
 *
 * @author trsimoes
 * @version $Revision:$
 */
public class HeartbeatStatistics implements HeartbeatStatisticsMBean {

    private static Log LOG = LogFactory.getLog(HeartbeatStatistics.class);

    private RequestDAO requestDAO;

    public HeartbeatStatistics() {
        this.requestDAO = SimplePersistenceManager.getInstance().getDaoInstance(RequestDAO.class);
    }

    @Override
    public long getOfflineTime(final MachineInfo machineInfo, final Date startDate, final Date endDate)
            throws ReportException {

        long totalOfflineTime = 0;

        List<OfflinePeriod> offlinePeriods = new ArrayList<OfflinePeriod>();

        try {

            List<Request> requestList = this.requestDAO.findRequestBetweenDates(machineInfo.getHost(),
                    machineInfo.getPort(), startDate, endDate);

            OfflinePeriod offlinePeriod = null;

            for (Request request : requestList) {
                if (!request.isResult()) {
                    // machine was offline. start new offline period
                    if (offlinePeriod == null) {
                        offlinePeriod = new OfflinePeriod();
                    }
                    offlinePeriod.addReport(request);
                } else {
                    // machine was online. offline period will be closed
                    if (offlinePeriod != null) {
                        offlinePeriods.add(offlinePeriod);
                        offlinePeriod = null;
                    }
                }
            }

            // if there's an offline period open, add it to the list
            if (offlinePeriod != null) {
                offlinePeriods.add(offlinePeriod);
            }

            // calculate the sum of the offline periods in millis
            for (OfflinePeriod _offlinePeriod : offlinePeriods) {
                totalOfflineTime += _offlinePeriod.getTotalOfflineTime();
            }

        } catch (SQLException e) {
            throw new ReportException(e);
        }

        return totalOfflineTime;
    }

    @Override
    public long getTodayOfflineTime() throws ReportException {

        try {
            MachineInfo machineInfo = CoreApplicationConfiguration.getConfiguredRemoteMachine();

            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            Date today = sdf.parse(sdf.format(new Date()));

            return getOfflineTime(machineInfo, today, null);
        } catch (ParseException e) {
            if (LOG.isErrorEnabled()) {
                LOG.error("could not generate current date", e);
            }
            throw new ReportException(e);
        }
    }

    @Override
    public int getNumberOfOfflinePeriodsToday() throws ReportException {

        int counter = 0;

        try {
            MachineInfo machineInfo = CoreApplicationConfiguration.getConfiguredRemoteMachine();

            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            Date today = sdf.parse(sdf.format(new Date()));

            List<Request> requestList = this.requestDAO.findRequestBetweenDates(machineInfo.getHost(),
                    machineInfo.getPort(), today, null);

            boolean offlinePeriod = false;


            for (Request request : requestList) {
                if (!request.isResult() && !offlinePeriod) {
                    // machine was offline. start new offline period
                    offlinePeriod = true;
                    counter++;
                } else if (request.isResult()) {
                    // machine was online.
                    offlinePeriod = false;
                }
            }

        } catch (SQLException e) {
            throw new ReportException(e);
        } catch (ParseException e) {
            throw new ReportException(e);
        }

        return counter;
    }
}